#include "ser_ir.h"
#include <8051.h>

#define XBUFLEN 4
#define RBUFLEN 8

/* You might want to specify idata, pdata or xdata for the buffers */
static unsigned char __pdata rbuf[RBUFLEN], xbuf[XBUFLEN];
static unsigned char rcnt, xcnt, rpos, xpos;
static __bit busy;

void ser_init( void ) {
  ES = 0;
  rcnt = xcnt = rpos = xpos = 0;  /* init buffers */
  busy = 0;
  SCON = 0x50;
  PCON |= 0x80;                   /* SMOD = 1; */
  TMOD &= 0x0f;                   /* use timer 1 */
  TMOD |= 0x20;
  TL1 = -3;
  TH1 = -3;
  TR1 = 1;    /* 19200bps with 11.059MHz crystal */
  ES = 1;
}

void ser_handler( void ) __interrupt 4 {
  if( RI ) {
    RI = 0;
    /* don't overwrite chars already in buffer */
    if( rcnt < RBUFLEN )
    { rbuf [( unsigned char )( rpos + rcnt++ ) % RBUFLEN] = SBUF; }
  }
  if( TI ) {
    TI = 0;
    if( busy = xcnt ) {  /* Assignment, _not_ comparison! */
      xcnt--;
      SBUF = xbuf [xpos++];
      if( xpos >= XBUFLEN )
      { xpos = 0; }
    }
  }
}

void ser_putc( unsigned char c ) {
  while( xcnt >= XBUFLEN ) /* wait for room in buffer */
    ;
  ES = 0;
  if( busy ) {
    xbuf[( unsigned char )( xpos + xcnt++ ) % XBUFLEN] = c;
  } else {
    SBUF = c;
    busy = 1;
  }
  ES = 1;
}

unsigned char ser_getc( void ) {
  unsigned char c;
  while( !rcnt )  /* wait for character */
    ;
  ES = 0;
  rcnt--;
  c = rbuf [rpos++];
  if( rpos >= RBUFLEN )
  { rpos = 0; }
  ES = 1;
  return ( c );
}

#pragma save
#pragma noinduction
void ser_puts( unsigned char *s ) {
  unsigned char c;
  while( c = *s++ ) {
    if( c == '\n' ) {
      ser_putc( '\r' );
    }
    ser_putc( c );
  }
}
#pragma restore

void ser_gets( unsigned char *s, unsigned char len ) {
  unsigned char pos, c;
  pos = 0;
  while( pos <= len ) {
    c = ser_getc();
    if( c == '\r' ) {
      continue;  /* discard CR's */
    }
    s[pos++] = c;
    if( c == '\n' ) {
      break;  /* NL terminates */
    }
  }
  s[pos] = '\0';
}

unsigned char ser_can_xmt( void ) {
  return XBUFLEN - xcnt;
}

unsigned char ser_can_rcv( void ) {
  return rcnt;
}
